﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Wicresoft.Shell.Package.AppExp;
using Wicresoft.Shell.Package.AppExp.ToolboxControl;

using UmcControls.DesignInterface;
using Wicresoft.Shell.Core;

namespace UmcProvider
{
    public sealed class CodeDomLoader
    {
        /// <summary>
        /// Serialize the design
        /// </summary>
        /// <param name="host"></param>
        /// <returns></returns>
        public XmlDocument Serialize(IDesignerHost host)
        {
            XmlDocument xml = new XmlDocument();
            //Get the root umcBody
            UmcBody umcBody = host.RootComponent as UmcBody;
            //the tag store the whole document before the last deserialize , see the deserialize function in UmcBody
            xml.LoadXml(umcBody.Tag.ToString());
            //find the old body from the pre document
            XmlNode oldBody = xml.SelectSingleNode("//" + UmcConstant.UmcBodyName);
            //serialize the new body from the design
            XmlNode body = umcBody.Serialize(umcBody, xml).First();
            XmlNode parent = oldBody.ParentNode;
            //replace the oldbody with the new
            parent.RemoveChild(oldBody);
            parent.AppendChild(body);

            //enum controls(since script is inherited from Control now , we can get scripts from umcBody.Controls)
            foreach (IComponent child in umcBody.Controls)
            {
                IConverter ict = DesignProvider.Create(child);
                if (ict != null)
                {
                    XmlNode[] nodes = ict.Serialize(child, xml);

                    foreach (var node in nodes)
                        body.AppendChild(node);
                }
            }
            return xml;
        }

        /// <summary>
        /// DeSerialize the code
        /// </summary>
        /// <param name="host"></param>
        /// <param name="xml"></param>
        public void DeSerialize(IDesignerHost host, XmlDocument xml)
        {
            IntelliSenseManager.ParseContext(xml.OuterXml, true);
            UmcBody umcBody = host.RootComponent as UmcBody;
            Debug.Assert(umcBody != null);

            List<IComponent> oldList = new List<IComponent>();
            List<IComponent> newList = new List<IComponent>();

            //delete controls and store them in the oldList in order to prevent exception
            foreach (IComponent obj in host.Container.Components.Cast<IComponent>())
            {
                //we cant delete UmcBody
                if (!(obj is UmcBody))
                {
                    oldList.Add(obj);
                    if (obj is Control)
                    {
                        //delete obj from umcBody
                        umcBody.Controls.Remove((Control)obj);
                        //delete obj from host(Tech:if component is create through host.CreateComponent() method , then
                        //it will be added in the host.Container , otherwise not!)
                        host.Container.Remove(obj);
                        //some container control may dont support the interface
                        IUmcPropertyStore iumcps = obj as IUmcPropertyStore;
                        if (iumcps != null)
                        {
                            (obj as IUmcPropertyStore).RemoveAllUmcProperties();    //clear the UmcProperties
                        }
                    }
                }
            }

            //set the body itself
            XmlNode xn = xml.SelectSingleNode("//" + UmcConstant.UmcBodyName);
            if (xn == null)
            {
                Logger.Output(string.Format("DeSerialize Error(CodeDomLoader.DeSerialize):Can't find {0}", UmcConstant.UmcBodyName), WriteTo.OutputWindow);
                throw new ArgumentException();
            }
            else
            {
                IComponent body = new UmcBody().DeSerialize(host, xml, new XmlNode[] { xn });
                UmcBody ubody = body as UmcBody;
                umcBody.Tag = ubody.Tag;
                umcBody.Size = ubody.Size;
            }

            //IComponent body = new UmcBody().DeSerialize(host, xml, new XmlNode[] { xn });
            //UmcBody ubody = body as UmcBody;
            //umcBody.Tag = ubody.Tag;
            //umcBody.Size = ubody.Size;


            try
            {
                //deal with every element from children of UmcBody
                foreach (XmlNode node in xml.SelectSingleNode("//" + UmcConstant.UmcBodyName).ChildNodes)
                {
                    IConverter ict = DesignProvider.Create(node.Name);
                    //if we have defined this kind of control , here ict should not be null , since each predefined control
                    //is implment IConverter
                    if (ict != null)
                    {
                        IComponent component = ict.DeSerialize(host, xml, new XmlNode[] { node });
                        newList.Add(component);
                    }
                    else //for some element not supported by the designer but appeared in the .umc file , we create a script
                    {
                        ict = DesignProvider.Create("Script");
                        //the script control will store all xml code in its Tag properties , see UmcScript definition
                        IComponent component = ict.DeSerialize(host, xml, new XmlNode[] { node });
                        newList.Add(component);
                    }
                }
            }
            catch(Exception ex)
            {
                //if any exception happens , we add old controls only
                foreach (IComponent obj in oldList)
                {
                    if (obj is Control)
                    {
                        umcBody.Controls.Add((Control)obj);
                        //host.Container.Add(obj);
                    }
                }
                throw ex;
            }

            foreach (IComponent obj in newList)
            {
                if (obj is Control)
                {
                    //in the method DeSerialize of each control , host.CreateComponent() will invoke , so the
                    //host is already finish deseralize , here we just add them into the umcBody
                    umcBody.Controls.Add((Control)obj);
                }
            }
        }
    }

}
